package basics;

import java.util.Iterator;

public class VectorMatrixUtils {

	private static class VMIterator implements Iterator<Double> {

		public VMIterator(VectorMatrix vm) {
			this.vm = vm;
		}

		private VectorMatrix vm;
		private int[] idx;
		boolean newRow = false;

		@Override
		public boolean hasNext() {
			if (idx == null) {
				prepareIdx();
			}

			for (int i = 0; i < vm.dimesnsion(); i++) {
				if (idx[i] >= vm.length(i)) {
					return false;
				}
			}

			return true;
		}

		@Override
		public Double next() {
			if (idx == null) {
				prepareIdx();
			}

			double d = vm.get(idx);
			moveForward();

			return d;

		}

		private void moveForward() {
			for (int i = 0; i < vm.dimesnsion(); i++) {
				if (idx[i] + 1 < vm.length(i) || i == vm.dimesnsion() - 1) {
					idx[i]++;
					newRow = false;
					break;
				} else if (i < vm.dimesnsion() - 1) {
					idx[i] = 0;
					newRow = true;
				}
			}
		}

		private void prepareIdx() {
			idx = new int[vm.dimesnsion()];
			for (int i = 0; i < vm.dimesnsion(); i++) {
				idx[i] = 0;
			}
		}

		@Override
		public void remove() {
		}

	};

	public static Iterator<Double> getIterator(final VectorMatrix vm) {
		return new VMIterator(vm);
	}

	public static String toString(VectorMatrix vm) {
		StringBuilder sr = new StringBuilder();
		VMIterator it = new VMIterator(vm);

		while (it.hasNext()) {
			sr.append(it.next());
			if (it.newRow) {
				sr.append("\n");
			} else {
				sr.append(",");
			}
		}
		return sr.substring(0, sr.length() - 1).toString();
	}

	public enum OperationOpt {
		Default, NonZero, AbsVal
	}

	public static double mean(VectorMatrix vm) {
		return mean(vm, OperationOpt.Default);
	}

	public static double mean(VectorMatrix vm, OperationOpt mm) {
		Iterator<Double> it = vm.iterator();
		double s = 0;
		int n = 0;
		while (it.hasNext()) {
			double next = it.next();
			if (mm == OperationOpt.Default || (mm == OperationOpt.NonZero && next != 0)) {
				s += next;
				n++;
			}
		}

		return s / n;
	}

	public static double stddev(VectorMatrix vm, double mean) {
		Iterator<Double> it = vm.iterator();
		double s = 0;
		int n = 0;
		while (it.hasNext()) {
			double next = it.next();
			s += Math.pow((next - mean), 2);
			n++;
		}

		return Math.sqrt(s / n);
	}

	public static double stddev(VectorMatrix vm) {
		return stddev(vm, mean(vm, OperationOpt.Default));
	}

	public static double min(VectorMatrix vm) {
		return min(vm, OperationOpt.Default);
	}

	public static double min(VectorMatrix vm, OperationOpt opt) {
		Iterator<Double> it = vm.iterator();
		double m = Double.MAX_VALUE;
		while (it.hasNext()) {
			double next = it.next();
			if (next < m && (opt == OperationOpt.Default || (opt == OperationOpt.NonZero && next != 0))) {
				m = next;
			}
		}
		return m;
	}

	public static double max(VectorMatrix vm) {
		return max(vm, OperationOpt.Default);
	}

	public static double max(VectorMatrix vm, OperationOpt opt) {
		Iterator<Double> it = vm.iterator();
		double m = Double.MIN_VALUE;
		while (it.hasNext()) {
			double next = it.next();
			if (next > m && (opt == OperationOpt.Default || (opt == OperationOpt.NonZero && next != 0))) {
				m = next;
			}
		}
		return m;
	}

	
}
